from group import group
from expr import expr
from quantifier import one, oneOrMore, zeroOrMore, opt
from proxy import proxy
from atom import atom
from lex.token import *
from c3e import machine

ATOM = group(map(one, (atom(NAME), atom(NUMBER), atom(STRING),
                       atom(COMMAND, 'falso'), atom(COMMAND, 'verdadeiro'))),
             func=machine.atom)

TYPES = group(map(one, (atom(COMMAND, 'literal'),
                        atom(COMMAND, 'logico'),
                        atom(COMMAND, 'real'),
                        atom(COMMAND, 'inteiro'))),
              func=machine.types)

STAR_SLASH = group(map(one, (atom(STAR), atom(SLASH))),
                   func=machine.atom)
_ATOM = expr(map(one, (STAR_SLASH, ATOM)),
             func=machine.promote)
TERM = expr((one(ATOM), zeroOrMore(_ATOM)),
            func=machine.promote,
            post=machine.term)

PLUS_MINUS = group(map(one, (atom(PLUS), atom(MINUS))),
                   func=machine.atom)
_TERM = expr(map(one, (PLUS_MINUS, TERM)),
             func=machine.promote)
EXPR = ARITH_EXPR = expr((one(TERM), zeroOrMore(_TERM)),
                         func=machine.promote,
                         post=machine.expr)

COMPOP = group(map(one, (atom(GREATER), atom(LESS),
                         atom(LESSEQUAL), atom(GREATEREQUAL),
                         atom(EQUAL), atom(NOTEQUAL))),
               func=machine.atom)
_EXPR = expr(map(one, (COMPOP, EXPR)),
             func=machine.promote)
COMP = expr((one(EXPR), zeroOrMore(_EXPR)),
            func=machine.promote,
            post=machine.comp)

_AND = atom(COMMAND, 'and', func=machine.atom)
_NOT = atom(COMMAND, 'not', func=machine.atom)
_AND_TEST = expr(map(one, (_AND, COMP)),
                 func=machine.promote)
_AND_NOT_TEST = expr(map(one, (_AND, _NOT, COMP)),
                     func=machine.promote)
AND_TEST = group(map(one, (_AND_TEST, _AND_NOT_TEST)),
                 func=machine.promote)
_OR = atom(COMMAND, 'or', func=machine.atom)
_OR_TEST = expr(map(one, (_OR, COMP)),
                func=machine.promote)
_OR_NOT_TEST = expr(map(one, (_OR, _NOT, COMP)),
                    func=machine.promote)
OR_TEST = group(map(one, (_OR_TEST, _OR_NOT_TEST)),
                func=machine.promote)
BOOL_TEST = group(map(one, (AND_TEST, OR_TEST)),
                  func=machine.promote)
TEST = expr((one(COMP), zeroOrMore(BOOL_TEST)),
            func=machine.promote,
            post=machine.test)

_NAME = atom(NAME, func=machine.atom)
_NAMES = expr(map(one, (atom(COMMA), _NAME)),
              func=machine.promote)

NAMES = expr((one(_NAME), zeroOrMore(_NAMES)),
             func=machine.promote)

READ_CMD = atom(COMMAND, 'leia', func=machine.atom)
READ_STMT = expr(map(one, (READ_CMD, NAMES)),
                 func=machine.promote,
                 post=machine.io)

_ATOM_OR_EXP = group(map(one, (COMP, ATOM)),
                     func=machine.promote)
_ATOMS = expr(map(one, (atom(COMMA), _ATOM_OR_EXP)),
              func=machine.promote)
ATOMS = expr((one(_ATOM_OR_EXP), zeroOrMore(_ATOMS)),
             func=machine.promote)

WRITE_CMD = atom(COMMAND, 'escreva', func=machine.atom)
WRITE_STMT = expr(map(one, (WRITE_CMD, ATOMS)),
                  func=machine.promote,
                  post=machine.io)

_ASSIGN = expr(map(one, (atom(NAME), atom(EQUAL))),
               func=machine.atom)
EXPR_STMT = expr(map(one, (_ASSIGN, TEST)),
                 func=machine.promote,
                 post=machine.assign)

SMALL_STMT = group(map(one, (READ_STMT, WRITE_STMT, EXPR_STMT)),
                   func=machine.promote,
                   post=machine.small_stmt)

_SIMPLE_STMT = expr((one(atom(SEMI, ';')), opt(atom(NL)), one(SMALL_STMT)),
                    func=machine.promote)
SIMPLE_STMT = expr((one(SMALL_STMT), zeroOrMore(_SIMPLE_STMT),
                    opt(atom(SEMI, ';')), oneOrMore(atom(NL))),
                   func=machine.promote,
                   post=machine.small_stmt)

SUITE = proxy(None)


IF = atom(COMMAND, 'se', func=machine.atom)
THEN = atom(COMMAND, 'entao', func=machine.atom)
ELSE = atom(COMMAND, 'senao', func=machine.atom)
END_IF = atom(COMMAND, 'fim_se', func=machine.atom)
ELIF_STMT = expr(map(one, (ELSE, IF, TEST)) +
                 [oneOrMore(atom(NL)), one(SUITE)],
                 func=machine.promote)
ELSE_STMT = expr((one(ELSE), oneOrMore(atom(NL)), one(SUITE)),
                 func=machine.promote)
IF_STMT = expr(map(one, (IF, TEST, THEN)) +
               [oneOrMore(atom(NL)), one(SUITE),
                zeroOrMore(ELIF_STMT), opt(ELSE_STMT),
                one(END_IF)],
               func=machine.promote,
               post=machine.if_stmt)

DO = atom(COMMAND, 'faca')
WHILE = atom(COMMAND, 'enquanto', func=machine.atom)
WHILE_STMT = expr(map(one, (DO, WHILE, TEST)) +
                  [oneOrMore(atom(NL)), one(SUITE),
                   one(atom(COMMAND, 'fim_enquanto'))],
                  func=machine.promote,
                  post=machine.while_stmt)

REPEAT = atom(COMMAND, 'repita', func=machine.atom)
UNTIL = atom(COMMAND, 'ate', func=machine.atom)
REPEAT_UNTIL_STMT = expr((one(REPEAT),
                          oneOrMore(atom(NL)), one(SUITE),
                          one(UNTIL), one(TEST)),
                         func=machine.promote,
                         post=machine.repeat_until_stmt)

_CASE_CMD = atom(COMMAND, 'caso', func=machine.atom)
_CASE = expr(map(one, (_CASE_CMD, TEST,
                       atom(COLON, ':'))) +
             [oneOrMore(atom(NL)), one(SUITE)],
             func=machine.promote)
_OTHER_CMD = atom(COMMAND, 'outro_caso', func=machine.atom)
_OTHER = expr(map(one, (_OTHER_CMD, atom(COLON, ':'))) +
              [oneOrMore(atom(NL)), one(SUITE)],
              func=machine.promote)
END_CASE = atom(COMMAND, 'fim_caso', func=machine.atom)
CASE_STMT = expr(map(one, (DO, _CASE_CMD)) +
                 [oneOrMore(atom(NL))] +
                 map(one, (_CASE_CMD, TEST, atom(COLON, ':'))) +
                 [oneOrMore(atom(NL)), one(SUITE),
                  zeroOrMore(_CASE), opt(_OTHER),
                  one(END_CASE)],
                 func=machine.promote,
                 post=machine.case_stmt)

_VAR_NAMES = expr(map(one, (atom(COMMA), atom(NAME))))
VAR_NAMES = expr((one(atom(NAME)), zeroOrMore(_VAR_NAMES)),
                 func=machine.var_names)
_VARS = expr(map(one, (VAR_NAMES, atom(COLON, ':'), TYPES, atom(NL))),
             post=machine.variaveis)
VARS_STMT = expr((one(atom(COMMAND, 'variaveis')), opt(atom(NL)),
                  oneOrMore(_VARS)))

_COMPOUND_STMT = group(map(one, (IF_STMT, WHILE_STMT,
                                 REPEAT_UNTIL_STMT, CASE_STMT)),
                       func=machine.promote)
COMPOUND_STMT = expr((one(_COMPOUND_STMT), oneOrMore(atom(NL))),
                     func=machine.promote)

STMT = group(map(one, (SIMPLE_STMT, COMPOUND_STMT)),
             func=machine.promote,
             post=machine.stmt)

_SUITE = group((one(SIMPLE_STMT), oneOrMore(STMT)),
               func=machine.promote,
               post=machine.stmt)
SUITE.set(_SUITE)

STMTS = group(map(one, (atom(NL), STMT)),
              func=machine.promote)
FILE_INPUT = expr((zeroOrMore(atom(NL)), one(atom(COMMAND, 'inicio')),
                   oneOrMore(atom(NL)),
                   opt(VARS_STMT),
                   zeroOrMore(atom(NL)),
                   oneOrMore(STMTS),
                   one(atom(COMMAND, 'fim')),
                   zeroOrMore(atom(NL)),
                   one(atom(ENDMARKER))),
                  post=machine.file_input)
